// Copyright (c) 2008-2010, Vincent Gable.
// http://vincentgable.com
//
// Modified by MonsieurDart aka Mathieu Godart - L'atelier du mobile to extract only the LOG_EXPR code.
//
// Based off http://www.dribin.org/dave/blog/archives/2008/09/22/convert_to_nsstring/

#import "TLog.h"

static BOOL TypeCodeIsCharArray(const char *typeCode){
    size_t len = strlen(typeCode);
    if(len <= 2)
        return NO;
    size_t lastCharOffset = len - 1;
    size_t secondToLastCharOffset = lastCharOffset - 1 ;
    
    BOOL isCharArray = typeCode[0] == '[' &&
    typeCode[secondToLastCharOffset] == 'c' && typeCode[lastCharOffset] == ']';
    for(int i = 1; i < secondToLastCharOffset; i++)
        isCharArray = isCharArray && isdigit(typeCode[i]);
    return isCharArray;
}

//since BOOL is #defined as a signed char, we treat the value as
//a BOOL if it is exactly YES or NO, and a char otherwise.
static NSString* VTPGStringFromBoolOrCharValue(BOOL boolOrCharvalue) {
    if(boolOrCharvalue == YES)
        return @"YES";
    if(boolOrCharvalue == NO)
        return @"NO";
    return [NSString stringWithFormat:@"'%c'", boolOrCharvalue];
}

static NSString *VTPGStringFromFourCharCodeOrUnsignedInt32(FourCharCode fourcc) {
    return [NSString stringWithFormat:@"%u ('%c%c%c%c')",
            (unsigned int)fourcc,
            (char)(fourcc >> 24) & 0xFF,
            (char)(fourcc >> 16) & 0xFF,
            (char)(fourcc >> 8) & 0xFF,
            (char)fourcc & 0xFF];
}

static NSString *StringFromNSDecimalWithCurrentLocal(NSDecimal dcm) {
    return NSDecimalString(&dcm, [NSLocale currentLocale]);
}

NSString * VTPG_DDToStringFromTypeAndValue(const char * typeCode, void * value) {
#define IF_TYPE_MATCHES_INTERPRET_WITH(typeToMatch,func) \
if (strcmp(typeCode, @encode(typeToMatch)) == 0) \
return (func)(*(typeToMatch*)value)
    
#if        TARGET_OS_IPHONE
    IF_TYPE_MATCHES_INTERPRET_WITH(CGPoint,NSStringFromCGPoint);
    IF_TYPE_MATCHES_INTERPRET_WITH(CGSize,NSStringFromCGSize);
    IF_TYPE_MATCHES_INTERPRET_WITH(CGRect,NSStringFromCGRect);
    IF_TYPE_MATCHES_INTERPRET_WITH(CGAffineTransform,NSStringFromCGAffineTransform);
#else
    IF_TYPE_MATCHES_INTERPRET_WITH(NSPoint,NSStringFromPoint);
    IF_TYPE_MATCHES_INTERPRET_WITH(NSSize,NSStringFromSize);
    IF_TYPE_MATCHES_INTERPRET_WITH(NSRect,NSStringFromRect);
#endif
    IF_TYPE_MATCHES_INTERPRET_WITH(NSRange,NSStringFromRange);
    IF_TYPE_MATCHES_INTERPRET_WITH(Class,NSStringFromClass);
    IF_TYPE_MATCHES_INTERPRET_WITH(SEL,NSStringFromSelector);
    IF_TYPE_MATCHES_INTERPRET_WITH(BOOL,VTPGStringFromBoolOrCharValue);
    IF_TYPE_MATCHES_INTERPRET_WITH(NSDecimal,StringFromNSDecimalWithCurrentLocal);
    
#define IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(typeToMatch,formatString) \
if (strcmp(typeCode, @encode(typeToMatch)) == 0) \
return [NSString stringWithFormat:(formatString), (*(typeToMatch*)value)]
    
    
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(CFStringRef,@"%@"); //CFStringRef is toll-free bridged to NSString*
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(CFArrayRef,@"%@"); //CFArrayRef is toll-free bridged to NSArray*
    IF_TYPE_MATCHES_INTERPRET_WITH(FourCharCode, VTPGStringFromFourCharCodeOrUnsignedInt32);
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long long,@"%lld");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned long long,@"%llu");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(float,@"%f");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(double,@"%f");
#if __has_feature(objc_arc)
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(__unsafe_unretained id,@"%@");
#else /* not __has_feature(objc_arc) */
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(id,@"%@");
#endif
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(short,@"%hi");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned short,@"%hu");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(int,@"%i");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned, @"%u");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long,@"%li");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long double,@"%Lf"); //WARNING on older versions of OS X, @encode(long double) == @encode(double)
    
    //C-strings
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(char*, @"%s");
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(const char*, @"%s");
    if(TypeCodeIsCharArray(typeCode))
        return [NSString stringWithFormat:@"%s", (char*)value];
    
    IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(void*,@"(void*)%p");
    
    //This is a hack to print out CLLocationCoordinate2D, without needing to #import <CoreLocation/CoreLocation.h>
    //A CLLocationCoordinate2D is a struct made up of 2 doubles.
    //We detect it by hard-coding the result of @encode(CLLocationCoordinate2D).
    //We get at the fields by treating it like an array of doubles, which it is identical to in memory.
    if(strcmp(typeCode, "{?=dd}")==0)//@encode(CLLocationCoordinate2D)
        return [NSString stringWithFormat:@"{latitude=%g,longitude=%g}",((double*)value)[0],((double*)value)[1]];
    
    //we don't know how to convert this typecode into an NSString
    return nil;
}